# frozen_string_literal: true

require 'spec_helper'

RSpec.describe VulnerabilityExports::Exporters::CsvService, feature_category: :vulnerability_management do
  include VulnerabilityExportsHelpers

  let(:export_csv_service) { described_class.new(Vulnerability.all) }

  subject(:csv) { CSV.parse(export_csv_service.generate, headers: true) }

  context 'when block is not given' do
    it 'renders csv to string' do
      expect(export_csv_service.generate).to be_a String
    end
  end

  context 'when block is given' do
    it 'returns handle to Tempfile' do
      expect(export_csv_service.generate { |file| file }).to be_a Tempfile
    end
  end

  it 'includes the columns required for import' do
    expect(csv.headers).to contain_exactly(*csv_headers)
  end

  describe 'CSV content' do
    context 'with valid findings' do
      before do
        vulnerability.finding.identifiers << create(:vulnerabilities_identifier, external_type: 'GSO', name: 'GSO-1234;1234')
        vulnerability.finding.identifiers << create(:vulnerabilities_identifier, external_type: 'TSO', name: 'TSO-1234')
      end

      context 'when a project belongs to a group' do
        let_it_be(:group) { create(:group) }
        let_it_be(:project) { project_with_valid_findings(group: group) }
        let_it_be(:vulnerability) { create(:vulnerability, :with_findings, project: project) }
        let_it_be(:note) { create(:note, project: project, noteable: vulnerability) }

        it 'includes proper values for each column type', :aggregate_failures do
          expect(csv[0]['Group Name']).to eq group.name
          expect(csv[0]['Project Name']).to eq project.name
          expect(csv[0]['Tool']).to eq vulnerability.report_type
          expect(csv[0]['Scanner Name']).to eq vulnerability.finding_scanner_name
          expect(csv[0]['Status']).to eq vulnerability.state
          expect(csv[0]['Vulnerability']).to eq vulnerability.title
          expect(csv[0]['Details']).to eq vulnerability.finding_description
          expect(csv[0]['Additional Info']).to be_nil
          expect(csv[0]['Severity']).to eq vulnerability.severity
          expect(csv[0]['CVE']).to eq vulnerability.cve_value
          expect(csv[0]['CWE']).to eq vulnerability.cwe_value
          expect(csv[0]['Other Identifiers']).to eq other_identifiers
          expect(csv[0]['Detected At']).to eq vulnerability.created_at.to_s
          expect(csv[0]['Location']).to eq vulnerability.location.to_s
          expect(csv[0]['Activity']).to eq vulnerability.resolved_on_default_branch.to_s
          expect(csv[0]['Comments']).to include(note.note)
          expect(csv[0]['Full Path']).to eq vulnerability.full_path
        end
      end

      context 'when a project belongs to a user' do
        let_it_be(:user) { create(:user) }
        let_it_be(:project) { project_with_valid_findings(namespace: user.namespace) }
        let_it_be(:vulnerability) { create(:vulnerability, :with_findings, project: project) }
        let_it_be(:note) { create(:note, project: project, noteable: vulnerability) }

        it 'includes proper values for each column except group name' do
          expect(csv[0]['Group Name']).to be_nil
          expect(csv[0]['Project Name']).to eq project.name
          expect(csv[0]['Tool']).to eq vulnerability.report_type
          expect(csv[0]['Scanner Name']).to eq vulnerability.finding_scanner_name
          expect(csv[0]['Status']).to eq vulnerability.state
          expect(csv[0]['Vulnerability']).to eq vulnerability.title
          expect(csv[0]['Details']).to eq vulnerability.finding_description
          expect(csv[0]['Additional Info']).to be_nil
          expect(csv[0]['Severity']).to eq vulnerability.severity
          expect(csv[0]['CVE']).to eq vulnerability.cve_value
          expect(csv[0]['CWE']).to eq vulnerability.cwe_value
          expect(csv[0]['Other Identifiers']).to eq other_identifiers
          expect(csv[0]['Detected At']).to eq vulnerability.created_at.to_s
          expect(csv[0]['Location']).to eq vulnerability.location.to_s
          expect(csv[0]['Activity']).to eq vulnerability.resolved_on_default_branch.to_s
          expect(csv[0]['Comments']).to include(note.note)
          expect(csv[0]['Full Path']).to eq vulnerability.full_path
        end
      end
    end

    context 'when a vulnerability is missing a finding' do
      let_it_be(:group) { create(:group) }
      let_it_be(:project) { project_with_valid_findings(group: group) }
      let_it_be(:vulnerability) { create(:vulnerability, project: project) }

      it 'includes proper values for each column except Other Identifiers' do
        expect(csv[0]['Group Name']).to eq group.name
        expect(csv[0]['Project Name']).to eq project.name
        expect(csv[0]['Tool']).to eq vulnerability.report_type
        expect(csv[0]['Scanner Name']).to eq vulnerability.finding_scanner_name
        expect(csv[0]['Status']).to eq vulnerability.state
        expect(csv[0]['Vulnerability']).to eq vulnerability.title
        expect(csv[0]['Details']).to eq vulnerability.finding_description
        expect(csv[0]['Additional Info']).to be_nil
        expect(csv[0]['Severity']).to eq vulnerability.severity
        expect(csv[0]['CVE']).to eq vulnerability.cve_value
        expect(csv[0]['CWE']).to eq vulnerability.cwe_value
        expect(csv[0]['Other Identifiers']).to be_nil
        expect(csv[0]['Detected At']).to eq vulnerability.created_at.to_s
        expect(csv[0]['Location']).to be_nil
        expect(csv[0]['Activity']).to eq vulnerability.resolved_on_default_branch.to_s
        expect(csv[0]['Comments']).to be_empty
        expect(csv[0]['Full Path']).to eq vulnerability.full_path
      end
    end
  end

  describe 'CSV headers' do
    context 'with en locale' do
      let_it_be(:group) { create(:group) }
      let_it_be(:project) { project_with_valid_findings(group: group) }
      let_it_be(:vulnerability) { create(:vulnerability, project: project) }

      it 'returns English headers' do
        ::Gitlab::I18n.with_locale('en') do
          expect(csv.headers).to eq(export_csv_service.send(:mapping).keys)
        end
      end
    end
  end
end
